/**
 * @file config.h
 * @brief ConfigWeave 核心类型、视图接口与构建/校验 API 的声明。
 * @details 提供不可变值模型 `Value`、对象/数组容器、`View` 读取接口、
 *          构建期 `Builder` 以及 Schema 规则与错误类型等，供应用与示例使用。
 */
#pragma once

#include "compat.h"

#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
#if CFG_HAVE_STD_RANGES
#include <ranges>
#endif

/// @namespace cfg
/// @brief ConfigWeave 的核心命名空间，包含所有公共类型与接口。
namespace cfg
{

// 预声明：用于在 View 接口中返回严格枚举的值类型
enum class SchemaValueType;

/**
 * @brief 错误码枚举，描述配置读取/校验过程中的常见错误类型。
 */
enum class Errc
{
    MissingKey,    ///< 缺少键或路径不存在
    TypeMismatch,  ///< 类型不匹配（对象/数组/标量之间或标量具体类型不符）
    OutOfRange,    ///< 数组索引越界或数值超范围
    InvalidPath,   ///< 非法路径（传统语法或 JSON Pointer）
    Other,         ///< 其他未归类错误
    RegexMismatch, ///< 字符串未匹配指定正则模式
    EnumNotInSet,  ///< 值不在枚举集合内
    InvalidRegex,  ///< 提供的正则表达式非法
    SchemaRootNotObject,     ///< Schema 根期望为对象但实际不是
    IncludeOpenError,        ///< 文件包含：打开失败
    IncludeParseError,       ///< 文件包含：解析失败
    IncludeUnexpectedError,  ///< 文件包含：其他未预期错误
    NumberExpected,          ///< 期望数值但实际不是
    StringExpectedForPattern ///< 正则匹配期望字符串但实际不是
};

/**
 * @brief 结构化错误信息，包含错误码、路径、期望/实际类型等上下文。
 */
struct ConfigError
{
    Errc code;                     ///< 错误码
    std::string message;           ///< 文本描述（例如："missing key 'db.host'"）
    std::vector<std::string> path; ///< 出错位置路径（例如：{"db","host"}）
    std::optional<SchemaValueType> expected_type = std::nullopt; ///< 期望的值类型
    std::optional<SchemaValueType> actual_type = std::nullopt;   ///< 实际检测到的值类型
    std::optional<double> min_value = std::nullopt; ///< 最小值（若启用范围约束）
    std::optional<double> max_value = std::nullopt; ///< 最大值（若启用范围约束）
    std::optional<std::string> expected_pattern = std::nullopt; ///< 期望的正则模式（字符串）
    std::optional<std::string> notes = std::nullopt;            ///< 额外备注信息
};

// ---------- 值类型（不可变） ----------
/**
 * @brief 前置声明：数组与对象容器。
 */
struct Array;
struct Object;

/**
 * @brief 配置值的不可变联合类型。
 * @details 包含 Null、Bool、Int64、Double、String 以及共享的只读 Array/Object。
 */
using Value = std::variant<std::nullptr_t,
                           bool,
                           int64_t,
                           double,
                           std::string,
                           std::shared_ptr<const Array>,
                           std::shared_ptr<const Object>>;

/**
 * @brief 只读数组容器，按值持有元素。
 */
struct Array
{
    std::vector<Value> items; ///< 冻结后不再修改
};

/**
 * @brief 只对象容器，键为字符串，值为 `Value`。
 */
struct Object
{
    std::unordered_map<std::string, Value> props; ///< 约定键小写且无空白，构建期规整
};

// ---- ranges 风格的便捷视图（非拥有，使用方需确保被引用对象存活） ----
/**
 * @brief 返回对象键的惰性视图。
 * @param object 目标对象
 * @return C++20 模式下为 ranges 视图（元素为 `std::string_view`）；
 *         C++17 模式下返回占位类型。
 */
inline auto
object_keys_view(const Object & object) noexcept
{
#if CFG_HAVE_STD_RANGES
    return object.props |
           std::views::transform([](const auto & kv) -> std::string_view { return kv.first; });
#else
    return as_view(object.props) |
           transform([](const auto & kv) -> std::string_view { return kv.first; });
#endif
}

/**
 * @brief 返回对象值的惰性视图。
 * @param object 目标对象
 * @return C++20 模式下为 ranges 视图（元素为 `const Value&`）；
 *         C++17 模式下返回占位类型。
 */
inline auto
object_values_view(const Object & object) noexcept
{
#if CFG_HAVE_STD_RANGES
    return object.props |
           std::views::transform([](const auto & kv) -> const Value & { return kv.second; });
#else
    return as_view(object.props) |
           transform([](const auto & kv) -> const Value & { return kv.second; });
#endif
}

/**
 * @brief 返回对象条目的惰性视图。
 * @param object 目标对象
 * @return C++20 模式下为 ranges 视图（元素为 `pair<string_view,const Value&>`）；
 *         C++17 模式下返回占位类型。
 */
inline auto
object_entries_view(const Object & object) noexcept
{
#if CFG_HAVE_STD_RANGES
    return object.props |
           std::views::transform(
               [](const auto & kv) {
                   return std::pair<std::string_view, const Value &>{kv.first, kv.second};
               });
#else
    return as_view(object.props) |
           transform(
               [](const auto & kv) {
                   return std::pair<std::string_view, const Value &>{kv.first, kv.second};
               });
#endif
}

/**
 * @brief 返回数组元素的视图。
 * @param array 目标数组
 * @return C++20 模式下为 `views::all`；C++17 模式下返回占位类型。
 */
inline auto
array_items_view(const Array & array) noexcept
{
#if CFG_HAVE_STD_RANGES
    return array.items | std::views::all;
#else
    return as_view(array.items);
#endif
}

// ---------- 视图 ----------
/**
 * @brief 只读视图，提供按路径/索引访问不可变配置值的接口。
 */
class View
{
public:
    /// @brief 构造根视图。
    explicit View(std::shared_ptr<const Object> root) noexcept;

    /// @brief 拷贝构造，保持当前节点语义。
    View(const View & other);

    /// @brief 移动构造。
    View(View && other) noexcept;

    /// @brief 拷贝赋值。
    View & operator=(const View & other);

    /// @brief 移动赋值。
    View & operator=(View && other) noexcept;

    /// @brief 逐段下钻对象属性。
    /// @param key 键名
    /// @return 子视图或错误
    expected<View, ConfigError> subobj(std::string_view key) const;

    /// @brief 按索引访问数组元素。
    /// @param index 索引
    /// @return 子视图或错误
    expected<View, ConfigError> at(size_t index) const;

    /// @brief 判断当前对象是否包含给定键。
    /// @param key 键名
    /// @return 存在返回 true
    bool has(std::string_view key) const noexcept;

    /// @brief 路径访问（传统语法或 JSON Pointer）。
    /// @param path 路径字符串
    /// @return 子视图或错误
    expected<View, ConfigError> lookup(std::string_view path) const;

    // 泛型取值（标量/容器视图）
    template <class T>
    /// @brief 泛型取值（T=std::string,int64_t,double,bool）。
    /// @return 期望类型的值或错误
    expected<T, ConfigError> get() const;

    /// @brief 数组视图访问。
    expected<span<const Value>, ConfigError> array_view() const;
    /// @brief 对象视图访问。
    expected<const Object *, ConfigError> object_view() const;

    /// @brief 获取整数（支持 double→int 范围/整数性检查）。
    expected<int64_t, ConfigError> get_int64() const;

    /// @brief 获取数值（支持 int→double 提升）。
    expected<double, ConfigError> get_number() const;

    /// @brief 获取字符串。
    expected<std::string, ConfigError> get_string() const;

    /// @brief 获取布尔值。
    expected<bool, ConfigError> get_bool() const;

    /// @brief 判断当前视图节点是否为 null。
    bool is_null() const;

    /// @brief 返回当前节点的具体值类型（严格）。
    std::optional<SchemaValueType> value_type() const;

    // 迭代（对象）
    struct Entry
    {
        std::string_view key; ///< 键名视图
        const Value & value;  ///< 值引用
    };

    /// @brief 返回对象的条目列表。
    expected<std::vector<Entry>, ConfigError> entries() const;

    /// @brief 返回当前位置路径。
    const std::vector<std::string> & path() const noexcept;

private:
    // 当前视图的所有权与定位信息
    std::shared_ptr<const Object> _root; // 持有根对象，确保整棵树的生命周期
    const Value * _current; // 指向当前节点的值；根对象用 _owned_value 承载
    std::unique_ptr<Value> _owned_value; // 在根视图处构造的合成 Value（持有 root 对象）
    std::vector<std::string> _path;      // 当前位置路径
};

// ---------- 构建期 策略配置 ----------
// 路径解析模式：
//  - AsProvided: 按传入路径使用（相对/绝对均不改动）
//  - RelativeToBaseDir: 若为相对路径，则相对 _include_base_dir 解析
/**
 * @brief include 文件路径解析模式。
 */
enum class IncludePathMode
{
    AsProvided,       ///< 按传入路径原样使用（相对/绝对不改动）
    RelativeToBaseDir ///< 相对路径基于 `_include_base_dir` 解析
};

// 合并策略：
//  - Shallow: 浅合并（后写覆盖先写或依据冲突策略）
//  - Deep: 深合并（对象递归合并）
/**
 * @brief 对象合并策略。
 */
enum class MergeStrategy
{
    Shallow, ///< 浅合并（后写覆盖或依据冲突策略）
    Deep     ///< 深合并（对象递归合并）
};

// 冲突处理策略：
//  - Overwrite: 后写覆盖
//  - Keep: 保留已存在值
//  - Error: 记录错误
/**
 * @brief 冲突处理策略。
 */
enum class ConflictPolicy
{
    Overwrite, ///< 后写覆盖
    Keep,      ///< 保留已存在值
    Error      ///< 记录错误
};

// 数组合并策略：
//  - Overwrite: 覆盖（源数组替换目标数组）
//  - Append: 追加（在目标数组末尾追加源数组元素）
//  - Keyed: 按键合并（对象数组按照某个键的值进行匹配并合并对象）
//  - Deduplicate: 去重（在追加时对标量与可比元素进行去重）
/**
 * @brief 数组合并策略。
 */
enum class ArrayMergeStrategy
{
    Overwrite,  ///< 覆盖（源数组替换目标数组）
    Append,     ///< 追加（在目标数组末尾追加源数组元素）
    Keyed,      ///< 按键合并（对象数组以某键值匹配并合并）
    Deduplicate ///< 去重（追加时对可比元素去重）
};

// ---------- Schema 规则定义 ----------
// 支持：必填、类型、数值范围（最小/最大，闭区间）、枚举、正则（字符串）。
// 规则基于路径（形如 "a.b[2].c"）。
/**
 * @brief 严格值类型枚举，用于 Schema 校验。
 */
enum class SchemaValueType
{
    Null,   ///< Null
    Bool,   ///< Bool
    Int64,  ///< 64 位整数
    Number, ///< 浮点数
    String, ///< 字符串
    Array,  ///< 数组
    Object  ///< 对象
};

/**
 * @brief Schema 规则定义：支持必填、类型、数值范围、枚举与正则约束。
 */
struct SchemaRule
{
    std::string path;      ///< 要校验的路径（例如："db.port"、"arr[0].x"）
    bool required = false; ///< 是否为必填项
    std::optional<SchemaValueType> expected_type; ///< 期望值类型（空表示不限制）
    bool has_min = false;                         ///< 是否启用最小值约束
    double min_value = 0.0;                       ///< 最小值（闭区间）
    bool has_max = false;                         ///< 是否启用最大值约束
    double max_value = 0.0;                       ///< 最大值（闭区间）
    std::vector<Value> enum_values; ///< 枚举允许集合（仅标量：bool/int64/double/string）
    std::string regex_pattern;      ///< 字符串正则（空字符串表示不启用）
};

// ---------- 构建期 ----------
/**
 * @brief 构建期 API：写入/合并/包含文件、策略配置、Schema 规则与冻结为只读对象。
 */
class Builder
{
public:
    /// @brief 写入路径上的值（传统语法），自动扩容数组。
    Builder & set(std::string path, Value v);

    // 作用：按 JSON Pointer（RFC 6901 子集）路径写入值。
    // 特性：
    //  - 路径必须以 '/' 开头，段按 '/' 分割；
    //  - 支持转义："~1" -> '/'，"~0" -> '~'；
    //  - 允许空段（表示空键名）；
    //  - 对数组段：段必须为非负整数字符串，自动扩容；
    //  - 中间节点自动创建（根据下一段是否为索引决定创建对象或数组）。
    //  - 若路径非法，则记录 Errc::InvalidPath 到 _errors。
    /// @brief 按 JSON Pointer（RFC 6901 子集）路径写入值。
    Builder & set_pointer(std::string path, Value v);

    // 合并对象（遵循合并策略与冲突策略）
    /// @brief 合并对象（遵循合并与冲突策略）。
    Builder & merge_object(const Object &);

    // 解析/加载插件点（支持路径解析模式）
    /// @brief 解析/加载插件点（支持路径解析模式）。
    Builder & include_file(std::string path);

    // 规则与验证
    /// @brief 设置键规整函数。
    Builder & normalize_keys(std::function<std::string(std::string_view)> f);

    /// @brief 设置 Schema 标识。
    Builder & schema(std::string schema_id);

    // Schema 规则 API
    // 添加一条基于路径的校验规则
    /// @brief 添加一条基于路径的校验规则。
    Builder & add_schema_rule(const SchemaRule & rule);

    // 清空已有的校验规则
    /// @brief 清空已有的校验规则。
    Builder & clear_schema_rules();

#ifdef CFG_ENABLE_YAML
    // 从 YAML 文件加载 JSON Schema（路径解析遵循 include 基础目录与模式）
    /// @brief 从 YAML 文件加载 JSON Schema（若启用）。
    Builder & load_json_schema_file(std::string path);
#else
    // 若未启用 YAML 支持，也保留 API 以便链接期报错更清晰
    /// @brief 占位 API（未启用 YAML 时仍提供）。
    Builder & load_json_schema_file(std::string path);
#endif

    // 策略配置
    /// @brief 设置合并策略。
    Builder & set_merge_strategy(MergeStrategy s);

    /// @brief 设置冲突处理策略。
    Builder & set_conflict_policy(ConflictPolicy p);

    /// @brief 设置 include 基础目录。
    Builder & set_include_base_dir(std::string dir);

    /// @brief 设置路径解析模式。
    Builder & set_include_path_mode(IncludePathMode m);

    /// @brief 设置数组合并策略。
    Builder & set_array_merge_strategy(ArrayMergeStrategy s);

    /// @brief 设置按键合并时的键名。
    Builder & set_array_merge_key(std::string key);

    /// @brief 冻结构建为只读对象；若存在错误则返回错误列表。
    expected<std::shared_ptr<const Object>, std::vector<ConfigError>> freeze();

private:
    Object _root_object;                                     // 构建中的可变根对象
    std::function<std::string(std::string_view)> _normalize; // 键规整函数
    std::string _schema_id;                                  // 可选 schema id
    std::vector<ConfigError> _errors;                        // 构建期累积错误

    // include 策略
    MergeStrategy _merge_strategy = MergeStrategy::Shallow;
    ConflictPolicy _conflict_policy = ConflictPolicy::Overwrite;
    IncludePathMode _include_path_mode = IncludePathMode::AsProvided;
    std::string _include_base_dir; // 仅在 RelativeToBaseDir 时生效

    // 数组合并策略
    ArrayMergeStrategy _array_merge_strategy = ArrayMergeStrategy::Overwrite;
    std::string _array_merge_key; // 用于 Keyed 模式

    // schema 规则集
    std::vector<SchemaRule> _schema_rules;
};

// ---------- View 模板方法实现 ----------
/**
 * @brief 泛型取值分派：根据模板参数调用具体强类型接口。
 * @tparam T 支持 `std::string`、`int64_t`、`double`、`bool`
 * @return 若类型匹配则返回值，否则返回错误。
 */
template <class T>
expected<T, ConfigError>
View::get() const
{
    static_assert(!std::is_same_v<T, void>, "T must not be void");

    if constexpr (std::is_same_v<T, std::string>)
    {
        return get_string();
    }
    else if constexpr (std::is_same_v<T, int64_t>)
    {
        return get_int64();
    }
    else if constexpr (std::is_same_v<T, double>)
    {
        return get_number();
    }
    else if constexpr (std::is_same_v<T, bool>)
    {
        return get_bool();
    }
    else
    {
        return unexpected(ConfigError{Errc::Other, "unsupported T for View::get", {}});
    }
}

} // namespace cfg